home *** CD-ROM | disk | FTP | other *** search
/ ADA Programming Guide / ADA Programming Guide.iso / ada_pcdp / adas / interp.pas < prev    next >
Pascal/Delphi Source File  |  1996-01-30  |  20KB  |  634 lines

  1. unit interp;
  2.  
  3.   { AdaS interpreter }
  4.  
  5. interface
  6. uses crt,global,util;
  7. procedure interpret;
  8.  
  9. implementation
  10.  
  11. procedure interpreter;
  12. const
  13.   stepmax  = 4;   { maximum steps executed between scheduler calls }
  14.   tru      = 1;   { internal representation of boolean values }
  15.   fals     = 0;
  16.   inactive = 999; { code for inactive process }
  17.  
  18. var
  19.   ps: (run, fin, divchk, inxchk, stkchk, redchk, deadlock);
  20.                      { processor status codes }
  21.   s: array[1..stmax] of integer;
  22.                      { the stack }
  23.  
  24.   ptab: array[ptype] of          { process table }
  25.          record
  26.           t:          integer;   { top of stack }
  27.           b:          integer;   { bottom of stack }
  28.           pc:         integer;   { program counter }
  29.           stacksize:  integer;   { size of stack segment }
  30.           display:    array[1..lmax] of integer;
  31.                                  { display of static links }
  32.           suspend:    integer;   { suspension pointer }
  33.           priority:   integer;   { priority }
  34.           timecalled: integer;   { time called for entry queues }
  35.           p1, p2:     integer;   { parameters of entry call }
  36.          end;
  37.  
  38.   ir:        order;     { current instruction being executed }
  39.   chrcnt:    integer;   { counter of characters in line }
  40.   npr:       ptype;     { number of active processes }
  41.   curpr:     ptype;     { current process }
  42.   stepcount: integer;   { count of steps in this time slice }
  43.   steps:     integer;   { number of steps until break }
  44.   selflag:   boolean;   { select is being executed }
  45.   pflag:     boolean;   { processes being activated }
  46.   selloop:   integer;   { loop count in select statement }
  47.   selrandom: integer;   { for random choice of alternative in select }
  48.   seltask:   ptype;     { task containing select statement }
  49.   deltaproc: integer;   { process index increment for scheduling }
  50.   stamp:     integer;   { internal clock for time stamp }
  51.   curent:    integer;   { current entry table index }
  52.   glovar: array[1..10] of integer;
  53.                         { global variable indices for watch }
  54.   numglo:    integer;   { number of entries in glovar }
  55.   ch:        char;      { temporary variables }
  56.   h1, h2, h3, h4: integer;
  57.  
  58. function itob(i: integer): boolean;
  59.   { integer to boolean }
  60. begin
  61.   itob := i=tru
  62. end;
  63.  
  64. function btoi(b: boolean): integer;
  65.   { boolean to integer }
  66. begin
  67.   if b then btoi := tru else btoi := fals
  68. end;
  69.  
  70. procedure getsteps;
  71.   { get command from break }
  72. begin
  73.   clreol;
  74.   deltaproc := 1;  { choose next active process in table }
  75.   stepcount := 0;
  76.   steps := 1;      { one step before next break }
  77.   write('Command: ');
  78.   ch := readkey;
  79.   if      ch = '+' then
  80.   else if ch = '*' then deltaproc := 0   { don't change process }
  81.   else if ch = '-' then steps := maxint  { execute indefinitely }
  82.   else if ch = '/' then ps := fin        { terminate interpretation }
  83.   else                                   { choose number of steps }
  84. {$I-}
  85.     repeat
  86.       write('Steps: ');
  87.       readln(steps);
  88.     until (ioresult = 0) and (steps > 0)
  89. {$I+}
  90. end;
  91.  
  92. procedure dump;
  93.   { called upon break and upon abnormal termination }
  94. var i,j: integer;
  95.     x,y: byte;
  96. begin
  97.   x := wherex; y := wherey;  { save program window coordinates }
  98.   window(1,13,40,25);        { write in dump window }
  99.   writeln;
  100.   with ptab[curpr] do
  101.     write('halt in process ', curpr:1, ' ');
  102.   clreol;
  103.   case ps of
  104.     run:       writeln('break');
  105.     deadlock:  writeln('deadlock');
  106.     divchk:    writeln('divsion by zero');
  107.     inxchk:    writeln('invalid index');
  108.     stkchk:    writeln('storage overflow');
  109.     redchk:    writeln('reading past eof');
  110.   end;
  111.   writeln('process suspend pc  instruction');
  112.   for i := 0 to pmax do
  113.     with ptab[i] do
  114.       begin
  115.       write(i:4, suspend:9, pc:5, code[pc].f:6, '  ');
  116.       printinst(output, code[pc].f);
  117.       writeln;
  118.       end;
  119.   writeln('entries');
  120.   for i := 1 to entries do
  121.    with entry[i] do
  122.       begin
  123.       write(name);
  124.       clreol;
  125.       if open <> 0 then write(' acceptor ', open:1,'/', waiting:1)
  126.       else
  127.         begin
  128.         write(' callers ');
  129.         for j := 1 to pmax do
  130.         if ptab[j].suspend = i then
  131.           write(j:1,'/',ptab[j].timecalled:1,'  ')
  132.         end;
  133.       writeln
  134.       end;
  135.   getsteps;            { get user command }
  136.   window(1,1,80,12);   { restore program window }
  137.   gotoxy(x,y)
  138. end;
  139.  
  140. procedure chooseproc;
  141.   { Scheduler:
  142.       starting with highest priority, search for a process
  143.       that is not suspended, then choose a time slice }
  144. var found: boolean;
  145. begin
  146.   h3 := pmax;  { highest priority }
  147.   h2 := (curpr + deltaproc) mod (pmax+1); { start search from here }
  148.   h1 := h2;
  149.   repeat
  150.     repeat
  151.       found := (ptab[h2].suspend = 0) and (ptab[h2].priority = h3);
  152.       h4 := h2;
  153.       h2 := (h2 + 1) mod (pmax + 1);
  154.     until found or (h2 = h1);
  155.     if not found then h3 := h3 - 1;  { next lower priority }
  156.   until found or (h3 = 0);
  157.   if h3 = 0 then ps := deadlock else curpr := h4;
  158.   stepcount := random(stepmax)   { choose random time slice }
  159. end;
  160.  
  161. procedure getpriorities;
  162.   { for each execution of the interpreter, individual priorities
  163.     may be set, otherwise all process have the same priority }
  164. begin
  165.   write('Priorities = ');
  166.   read(h1);
  167.   if h1 <> 0 then
  168.     begin
  169.     readln(h2, h3);
  170.     ptab[1].priority := h1;
  171.     ptab[2].priority := h2;
  172.     ptab[3].priority := h3
  173.     end
  174. end;
  175.  
  176. procedure initinterpret;
  177.   { initialization }
  178. var c: ptype;
  179.     i: integer;
  180. begin
  181.   s[1] := 0;          { environment activation record }
  182.   s[2] := 0;
  183.   s[3] := -1;
  184.   s[4] := btab[1].last;
  185.  
  186.   with ptab[0] do     { main process }
  187.     begin
  188.     b := 0;
  189.     suspend := 0;     { initially active }
  190.     priority := pmax;
  191.     display[1] := 0;
  192.     t := btab[2].vsize-1;
  193.     pc := tab[s[4]].adr;
  194.     stacksize := stmax - pmax*stkincr
  195.     end;
  196.  
  197.   for c := 1 to pmax do  { other processes }
  198.     with ptab[c] do
  199.       begin
  200.       display[1] := 0;
  201.       pc := 0;
  202.       priority := pmax;          { default priority }
  203.       suspend := inactive;       { initially inactive }
  204.       b := ptab[c-1].stacksize+1;
  205.       stacksize := b+stkincr-1;
  206.       t := b-1
  207.       end;
  208.  
  209.   stamp     := 0;
  210.   npr       := 0;
  211.   curpr     := 0;
  212.   seltask   := 0;
  213.   selrandom := 0;
  214.   selloop   := 2;
  215.   pflag     := false;
  216.   selflag   := false;
  217.   stepcount := 0;
  218.   ps        := run;
  219.   chrcnt    := 0;
  220.   steps     := 0;
  221.   numglo    := 0;
  222.   for i := 1 to entries do
  223.     with entry[1] do
  224.       begin open := 0; waiting := 0 end;
  225.   for i := 1 to 10 do glovar[i] := 0;
  226.   randomize;     { set random number generator }
  227.   getpriorities;
  228.   clrscr;
  229.   window(1,1,80,12);   { program window }
  230. end;
  231.  
  232. procedure relinquish(i: integer);
  233.   { relinquish the processor by suspending on i and forcing
  234.     a call to the scheduler }
  235. begin
  236.   ptab[curpr].suspend := i;
  237.   stepcount := 0
  238. end;
  239.  
  240. begin { interpret }
  241.   initinterpret;
  242.  
  243.   repeat
  244.     if keypressed then   { pressing any key forces break }
  245.       begin ch := readkey; steps := 0 end;
  246.     if steps = 0 then dump;
  247.     steps := steps - 1;
  248.  
  249.     if ptab[0].suspend = 0 then curpr := 0
  250.        { highest priority to main program to allow activation }
  251.     else if stepcount = 0 then chooseproc
  252.     else stepcount := stepcount - 1;
  253.  
  254.     with ptab[curpr] do  { extract next instruction }
  255.       begin
  256.       ir := code[pc];
  257.       pc := pc + 1
  258.       end;
  259.  
  260.     if pflag then  { process being activated }
  261.       begin
  262.       if ir.f=18 { markstack } then npr := npr + 1;
  263.       curpr := npr
  264.       end;
  265.  
  266.     with ptab[curpr] do
  267.     case ir.f of         { decode instruction }
  268.  
  269.     0:  begin { load address }
  270.         t := t + 1;
  271.         if t > stacksize then ps := stkchk
  272.         else s[t] := display[ir.x] + ir.y
  273.         end;
  274.  
  275.     1:  begin { load value }
  276.         t := t + 1;
  277.         if t > stacksize then ps := stkchk
  278.         else s[t] := s[display[ir.x] + ir.y]
  279.         end;
  280.  
  281.     2:  begin { load indirect }
  282.         t := t + 1;
  283.         if t > stacksize then ps := stkchk
  284.         else s[t] := s[s[display[ir.x] + ir.y]]
  285.         end;
  286.  
  287.     3:  begin { update display }
  288.         h1 := ir.y;
  289.         h2 := ir.x;
  290.         h3 := b;
  291.         repeat
  292.           display[h1] := h3;
  293.           h1 := h1 - 1;
  294.           h3 := s[h3+2]
  295.         until h1 = h2
  296.         end;
  297.  
  298.     4:  pflag := true; { cobegin - activate processes }
  299.  
  300.     5:  begin { coend - all processes activated }
  301.         pflag := false;
  302.         ptab[0].suspend := inactive
  303.         end;
  304.  
  305.     6:  begin { semaphore wait }
  306.         h1 := s[t];
  307.         t := t - 1;
  308.         if s[h1] > 0 then s[h1] := s[h1] - 1 else relinquish(h1)
  309.         end;
  310.  
  311.     7:  begin { semaphore signal }
  312.         h1 := s[t];
  313.         t := t - 1;
  314.         h2 := pmax+1;
  315.         h3 := random(h2);      { from random point }
  316.         while (h2 >= 0) and (ptab[h3].suspend <> h1) do
  317.           begin      { search for process suspended on this semaphore }
  318.           h3 := (h3+1) mod (pmax+1);
  319.           h2 := h2 - 1
  320.           end;
  321.         if h2 < 0 then s[h1] := s[h1] + 1  { if none then increment }
  322.         else ptab[h3].suspend := 0         { release suspended process }
  323.         end;
  324.  
  325.     10: pc := ir.y; { jump }
  326.  
  327.     11: begin { conditional jump }
  328.         if s[t] = fals then pc := ir.y;
  329.         t := t - 1
  330.         end;
  331.  
  332.     14: begin { top of for loop }
  333.         h1 := s[t-1];  { lower bound on index }
  334.         if h1 <= s[t] then s[s[t-2]] := h1 else
  335.           begin        { upper > lower so skip loop }
  336.           t := t - 3;
  337.           pc := ir.y
  338.           end
  339.         end;
  340.  
  341.     15: begin { bottom of for loop }
  342.         h2 := s[t-2];    { upper bound }
  343.         h1 := s[h2] + 1; { index }
  344.         if h1 <= s[t] then
  345.           begin          { jump to top }
  346.           s[h2] := h1;
  347.           pc := ir.y
  348.           end
  349.         else t := t - 3  { finished }
  350.         end;
  351.  
  352.     18: begin { mark stack }
  353.         h1 := btab[tab[ir.y].ref].vsize; { size of stack for call }
  354.         if t+h1 > stacksize then ps := stkchk else
  355.           begin
  356.           t := t + 5;       { allocate room for activation record }
  357.           s[t-1] := h1 - 1; { store size and tab index }
  358.           s[t] := ir.y      {   for call instruction }
  359.           end
  360.         end;
  361.  
  362.         { actual parameters stacked between mark stack and call }
  363.  
  364.     19: begin { procedure call }
  365.         suspend := 0;
  366.         h1 := t - ir.y;         { old bottom of stack }
  367.         h2 := s[h1+4];          { tab index left by mark stack }
  368.         h3 := tab[h2].lev;      { get nesting level }
  369.         display[h3+1] := h1;    { store in display }
  370.         h4 := s[h1+3] + h1;     { stack size left by mark stack }
  371.         s[h1+1] := pc;          { return address }
  372.         s[h1+2] := display[h3]; { static link }
  373.         if pflag then s[h1+3] := ptab[0].b else s[h1+3] := b;
  374.                                 { dynamic link }
  375.         for h3 := t+1 to h4 do s[h3] := 0;
  376.                                 { zero local variables }
  377.         b := h1;                { new bottom of stack }
  378.         t := h4;                { new top of stack }
  379.         pc := tab[h2].adr       { start of procedure code }
  380.         end;
  381.  
  382.     21: begin { load array element given index }
  383.         h1 := ir.y;
  384.         h2 := atab[h1].low;
  385.         h3 := s[t];
  386.         if h3 < h2 then ps := inxchk else
  387.           begin
  388.           t := t - 1;
  389.           s[t] := s[t] + (h3-h2) * atab[h1].elsize
  390.           end
  391.         end;
  392.  
  393.     24: begin { literal }
  394.         t := t + 1;
  395.         if t > stacksize then ps := stkchk else s[t] := ir.y
  396.         end;
  397.  
  398.     27: begin { read }
  399.         if eof(inp) then ps := redchk else
  400.           case ir.y of
  401.             1: read(inp, s[s[t]]);
  402.             3: begin read(inp, ch); s[s[t]] := ord(ch) end
  403.           end;
  404.         t := t - 1
  405.         end;
  406.  
  407.     28: begin { write string }
  408.         h1 := s[t];
  409.         h2 := ir.y;
  410.         t := t - 1;
  411.         chrcnt := chrcnt + h1;
  412.         if chrcnt = 80 then begin writeln; chrcnt := 0 end;
  413.         repeat
  414.           write(stab[h2]);
  415.           h1 := h1 - 1;
  416.           h2 := h2 + 1
  417.         until h1 = 0
  418.         end;
  419.  
  420.     29: begin { write1 }
  421.         if ir.y = 3 then h1 := 1 else h1 := 10;
  422.         chrcnt := chrcnt + h1;
  423.         if chrcnt = 80 then begin writeln; chrcnt := 0 end;
  424.         case ir.y of
  425.           1: write(s[t]);
  426.           2: write(itob(s[t]));
  427.           3: if (s[t]<0) or (s[t]>255) then ps := inxchk
  428.                else write(chr(s[t]))
  429.         end;
  430.         t := t - 1
  431.         end;
  432.  
  433.     31: { end of program } ps := fin;
  434.  
  435.     32: { exit procedure } begin
  436.         t := b - 1;        { old top of stack }
  437.         pc := s[b+1];      { return address }
  438.         if pc <> 0 then b := s[b+3] else
  439.                            { old bottom of stack from dynamic link }
  440.           begin            { exit from process }
  441.           if selflag then ptab[seltask].suspend := 0;
  442.           selloop := 2;
  443.           relinquish(inactive); { deactivate process }
  444.           npr := npr - 1;  { one less process active }
  445.           if npr=0 then ptab[0].suspend := 0
  446.              { if last process, reactivate main }
  447.           end
  448.         end;
  449.  
  450.     34: s[t] := s[s[t]];  { from address get value, used with index }
  451.     35: s[t] := btoi(not(itob(s[t])));  { boolean not }
  452.     36: s[t] := - s[t];   { unary minus }
  453.  
  454.     38: begin { store }
  455.         if ir.y <> 0 then  { watch variable }
  456.           begin
  457.           h1 := wherex; h2 := wherey; { save program window }
  458.           window(41,13,80,25);        { watch window }
  459.           h4 := numglo + 1;           { see if variable exists in table }
  460.           for h3 := 1 to numglo do
  461.             if ir.y = glovar[h3] then
  462.               h4 := h3;
  463.           if h4 = numglo+1 then       { create new table entry }
  464.             begin
  465.             numglo := h4;
  466.             glovar[numglo] := ir.y
  467.             end;
  468.           gotoxy(1,h4+1);             { table index is line in window }
  469.           writeln(tab[ir.y].name, s[t]:8);
  470.           window(1,1,80,12);          { reset window }
  471.           gotoxy(h1,h2)
  472.           end;
  473.         s[s[t-1]] := s[t];
  474.         t := t - 2;
  475.         end;
  476.  
  477.         { arithmetical and logical operators }
  478.  
  479.     45: begin t:=t-1; s[t] := btoi(s[t] =  s[t+1]) end;
  480.     46: begin t:=t-1; s[t] := btoi(s[t] <> s[t+1]) end;
  481.     47: begin t:=t-1; s[t] := btoi(s[t] <  s[t+1]) end;
  482.     48: begin t:=t-1; s[t] := btoi(s[t] <= s[t+1]) end;
  483.     49: begin t:=t-1; s[t] := btoi(s[t] >  s[t+1]) end;
  484.     50: begin t:=t-1; s[t] := btoi(s[t] >= s[t+1]) end;
  485.  
  486.     51: begin t:=t-1; s[t] := btoi(itob(s[t]) or  itob(s[t+1])) end;
  487.     52: begin t:=t-1; s[t] := s[t] + s[t+1] end;
  488.     53: begin t:=t-1; s[t] := s[t] - s[t+1] end;
  489.     56: begin t:=t-1; s[t] := btoi(itob(s[t]) and itob(s[t+1])) end;
  490.     57: begin t:=t-1; s[t] := s[t] * s[t+1] end;
  491.  
  492.     58: begin
  493.         t := t - 1;
  494.         if s[t+1] = 0 then ps := divchk else
  495.           s[t] := s[t] div s[t+1]
  496.         end;
  497.  
  498.     59: begin
  499.         t := t - 1;
  500.         if s[t+1] = 0 then ps := divchk else
  501.           s[t] := s[t] mod s[t+1]
  502.         end;
  503.  
  504.     62: { readln } if eof(inp) then ps := redchk else readln(inp);
  505.     63: { writeln } begin writeln; chrcnt := 0 end;
  506.  
  507.         { Before an entry call, the parameters are compiled
  508.           and the appropriate instruction 70-73 is emitted.
  509.           in parameters load the value into the fields p1, p2
  510.           of the calling process table entry while out
  511.           parameters load the address into those fields }
  512.  
  513.     70: begin p1 := s[t]; t := t - 1 end;   { load in parm 1 }
  514.     71: begin p2 := s[t]; t := t - 1 end;   { load in parm 2 }
  515.     72: p1 := display[ir.x]+ir.y;     { load out parm 1 }
  516.     73: p2 := display[ir.x]+ir.y;     { load out parm 2 }
  517.  
  518.     74: begin { call entry }
  519.           stamp := stamp + 1;   { time stamp this call }
  520.           timecalled := stamp;
  521.           with entry[ir.y] do
  522.           if open <> 0 then       { there is a waiting accept }
  523.             with ptab[waiting] do { waiting contains the process }
  524.               begin               { index of the accepting task }
  525.               pc := open;         { open contains the pc of the accept }
  526.               open := 0;          { revoke wait status }
  527.               suspend := 0;       { reactivate accepting task }
  528.               waiting := curpr    { store calling index here }
  529.               end
  530.           else { no waiting accept }
  531.             if waiting = 0 then waiting := curpr;
  532.                { if no other calls, we are first on this entry queue }
  533.         if selflag then ptab[seltask].suspend := 0;
  534.            { reactivate task with select }
  535.         selloop := 2;
  536.         relinquish(ir.y);         { calling task always suspended }
  537.         end;
  538.  
  539.         { A select statement will try each accept statement in
  540.           turn to see if there is a waiting call, otherwise it
  541.           will suspend itself.
  542.           To implement random selection of an alternative,
  543.           a random number is used to decide if the first accept
  544.           statement should be skipped. Since the second accept
  545.           statement may be closed or have an empty queue,
  546.           two passes are taken around the select loop before
  547.           deciding to suspend. }
  548.  
  549.     75: begin { accept entry }
  550.         curent := ir.y;
  551.         with entry[ir.y] do
  552.           if waiting = 0 then   { if no entry call waiting }
  553.             if selflag then     { executing select }
  554.               begin
  555.               pc := ir.x;       { jump over accept body }
  556.               selloop := selloop - 1
  557.               end
  558.             else                { no select }
  559.               begin
  560.               open := pc;       { note pc of waiting accept }
  561.               waiting := curpr; { and accepting process index }
  562.               relinquish(ir.y); { suspend pending an entry call }
  563.               end
  564.           else if selflag and (selrandom > 0) then
  565.             begin
  566.             pc := ir.x;         { randomly jump over accept body }
  567.             selrandom := 0
  568.             end
  569.         end;
  570.  
  571.         { When entering rendezvous, copy in parameters (76-77)
  572.           from calling task's process table fields p1 and p2.
  573.           When completing rendezvous, use addresses in those
  574.           fields to copy back the values (78-79). }
  575.  
  576.     76: s[display[ir.x]+ir.y] := ptab[entry[curent].waiting].p1;
  577.     77: s[display[ir.x]+ir.y] := ptab[entry[curent].waiting].p2;
  578.     78: s[ptab[entry[curent].waiting].p1] := s[display[ir.x]+ir.y];
  579.     79: s[ptab[entry[curent].waiting].p2] := s[display[ir.x]+ir.y];
  580.  
  581.     80: begin  { release call }
  582.         h1 := ir.y;
  583.         with entry[h1] do
  584.           begin
  585.           ptab[waiting].suspend := 0;  { calling task reactivated }
  586.           h4 := maxint;   { earliest call becomes waiting call }
  587.           h3 := 0;
  588.           for h2 := 1 to pmax do
  589.             if (ptab[h2].suspend = h1) and
  590.                (ptab[h2].timecalled < h4) then
  591.                  begin
  592.                  h4 := ptab[h2].timecalled;
  593.                  h3 := h2
  594.                  end;
  595.           waiting := h3
  596.           end
  597.         end;
  598.  
  599.     81: begin { select }
  600.         selflag := true;         { select being executed }
  601.         selrandom := random(2);  { random choice of alternative }
  602.         selloop := 2;            { loop count }
  603.         seltask := curpr         { process executing select }
  604.         end;
  605.  
  606.     82: { terminate }
  607.         if npr = 1 then selflag := false { last process so terminate }
  608.         else pc := pc + 1;               { skip over exit instruction }
  609.  
  610.     83: { end select } if selloop = 0 then relinquish(inactive)
  611.            { after twice around loop we can suspend }
  612.  
  613.     end { case };
  614.   until ps <> run;
  615.  
  616.   writeln;
  617.   if ps <> fin then dump
  618. end;
  619.  
  620. procedure interpret;
  621.   { Interpret the program in the code table }
  622. var ch: char;
  623. begin
  624.   repeat
  625.     write('Interpret (y/n): ');
  626.     if eoln then readln;
  627.     readln(ch);
  628.     if ch = 'y' then interpreter
  629.   until ch <> 'y';
  630.   window(1,1,80,25);
  631.   clrscr
  632. end;
  633.  
  634. end.